package com.sky.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.NumberUtil;
import com.sky.dto.OrderQuotaAggeDTO;
import com.sky.dto.OrderReportAggrDTO;
import com.sky.dto.OrdersAggrDataDTO;
import com.sky.entity.Orders;
import com.sky.exception.BaseException;
import com.sky.mapper.AggrDataDTOMapper;
import com.sky.result.Result;
import com.sky.service.IDateService;
import com.sky.service.IDishService;
import com.sky.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.output.ThresholdingOutputStream;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;

/**
 * @BelongsProject: sky-take-out
 * @Author: XieMingHui
 * @CreateTime: 2023-11-22  07:58
 * @Description: 数据统计处理层类
 * @Version: 1.0
 */
@Service
@Slf4j
public class IDateServiceImpl implements IDateService {
    @Resource
    AggrDataDTOMapper aggrDataDTOMapper;
    @Resource
    IDishService iDishService;


    /**
     * 查询销量排名10
     *
     * @author: XieMingHui
     * @param: [统计排名的时间]
     * @return: 返回菜品排名和金额排名字符串的对象
     **/
    @Override
    public Result<SalesTop10ReportVO> top(LocalDate begin, LocalDate end) {
        // 根据时间范围查询数据库所有已完成的订单的菜品名称和金额,根据菜名分组
        List<OrdersAggrDataDTO> ordersAggrDataDTOS = aggrDataDTOMapper
                .dataAggr(begin, end.plusDays(1L), Orders.COMPLETED);
        if (CollUtil.isEmpty(ordersAggrDataDTOS)) {
            return Result.success(new SalesTop10ReportVO("", ""));
        }
        //如果销量不为空
        StringJoiner sjName = new StringJoiner(",");
        StringJoiner sjAmount = new StringJoiner(",");
        for (OrdersAggrDataDTO dataDTO : ordersAggrDataDTOS) {
            sjName.add(dataDTO.getName());
            sjAmount.add("" + dataDTO.getMoney());
        }
        // 封装对象返回前端
        SalesTop10ReportVO top10ReportVO = new SalesTop10ReportVO(sjName.toString(), sjAmount.toString());
        log.info("菜品销量排行数据:{}", top10ReportVO);
        return Result.success(top10ReportVO);
    }

    /**
     * 统计营业额
     *
     * @author: XieMingHui
     * @param: [统计起始时间]
     * @return: TurnoverReportVO
     **/
    @Override
    public Result<TurnoverReportVO> turnoverStatistics(LocalDate begin, LocalDate end) {
        //// 创建map集合统计一个时间段的营业额
        LocalDate beginTemp = begin;
        Map<LocalDate, BigDecimal> result = new TreeMap<>();
        while (!beginTemp.isAfter(end)) {
            // 时间为key,营业为value并设置初始营业额为0
            result.put(beginTemp, BigDecimal.valueOf(0.00));
            beginTemp = beginTemp.plusDays(1);
        }
        // 把时间转成字符串
        Set<LocalDate> dates = result.keySet();
        String timeStr = CollUtil.join(dates, ",");
        // 根据时间查询数据库
        List<OrderQuotaAggeDTO> orderQuotaAggeDTOS = aggrDataDTOMapper
                .dataQuotaAggr(begin, end.plusDays(1L), Orders.COMPLETED);
        log.info("营业额数据查询结果:{}", orderQuotaAggeDTOS);
        if (CollUtil.isNotEmpty(orderQuotaAggeDTOS)) {
            // 如果结果不为空,遍历查询结果,根据时间存入营业额到value中
            orderQuotaAggeDTOS.forEach(orderQuotaAggeDTO -> {
                LocalDate orderTime = orderQuotaAggeDTO.getOrderTime();
                BigDecimal amount = orderQuotaAggeDTO.getAmount();
                // 如果不存在,则put,如果存在则根据参数方法运算后再put
                result.merge(orderTime, amount, NumberUtil::add);
            });
        }
        // 获取营业额拼接字符串,间隔符:,
        StringJoiner sJ = new StringJoiner(",");
        result.forEach((s, bigDecimal) -> sJ.add(bigDecimal + ""));
        String monyList = sJ.toString();
        // 封装对象返回前端
        TurnoverReportVO turnoverReportVO = new TurnoverReportVO(timeStr, monyList);
        log.info("营业额统计结果:{}", turnoverReportVO);
        return Result.success(turnoverReportVO);
    }

    /**
     * 订单统计
     *
     * @author: XieMingHui
     * @param: []
     * @return: OrderReportVO
     **/
    @Override
    public Result<OrderReportVO> ordersStatistics(LocalDate begin, LocalDate end) {
        // 查询数据库所有订单
        List<OrderReportAggrDTO> orderAggrList = aggrDataDTOMapper
                .dataOrderAggr(begin, end);
        if (orderAggrList.size() == 0) {
            throw new RuntimeException("没有订单");
        }
        // 定义整理数据库查询数据,计算,并封装成VO对象的方法
        OrderReportVO orderReportVO = getOrderReportVO(orderAggrList, begin, end);
        return Result.success(orderReportVO);
    }

    // 定义方法生成用于一个时间段的每日统计的集合
    public TreeMap<LocalDate, Integer> statsOrder(LocalDate begin, LocalDate end) {
        //// 调用自定义方法创建map集合统计一个时间段的有效订单数
        TreeMap<LocalDate, Integer> resultMap = new TreeMap<>();
        while (!begin.isAfter(end)) {
            // 时间为key,营业为value并设置初始营业额为0
            resultMap.put(begin, 0);
            begin = begin.plusDays(1);
        }
        return resultMap;
    }

    /**
     * 统计用户数
     *
     * @author: XieMingHui
     * @param: [begin, end]
     * @return: com.sky.result.Result<com.sky.vo.UserReportVO>
     **/
    @Override
    public Result<UserReportVO> userStatistics(LocalDate begin, LocalDate end) {
        // 得到总用户集合
        TreeMap<LocalDate, Integer> totalUser = statsOrder(begin, end);
        // 得到新用户集合
        TreeMap<LocalDate, Integer> newUser = statsOrder(begin, end);
        // 时间段集合
        Set<LocalDate> localDates = totalUser.keySet();
        //查询数据库
        List<LocalDate> createTimeList = aggrDataDTOMapper.totalUserList(end);
        // 如果查询有数据则统计数据没如果没有直接就返回初始的0数据
        if (CollUtil.isNotEmpty(createTimeList)) {
            // 在统计时间段之前就注册老用户统计
            int oldUserNum = 0;
            // 遍历数据统计开始时间以前的老用户和时间内的新用户
            for (LocalDate date : createTimeList) {
                if (date.isBefore(begin)) {
                    // 不在时间范围的老用户:注册时间在开始时间之前
                    oldUserNum++;
                } else {
                    // 在时间范围注册的用户,添加到新用户集合
                    newUser.put(date, newUser.get(date) + 1);
                }
            }
            for (LocalDate date : localDates) {
                // 当天的总用户数就是前一天总用户+当天的新增用户数,第一天就是时间范围之外的总数量oldUserNum+当天新增
                int yesterdayTotal = oldUserNum + newUser.get(date);
                totalUser.put(date, yesterdayTotal);
            }
        }
        // 将统计数据转换成字符串
        StringJoiner sjNew = new StringJoiner(",");
        newUser.forEach((k, v) -> sjNew.add("" + v));
        StringJoiner sjTotal = new StringJoiner(",");
        totalUser.forEach((k, v) -> sjTotal.add("" + v));
        // 封装属性返回前端
        UserReportVO userReportVO = UserReportVO.builder()
                .dateList(CollUtil.join(localDates, ","))
                .totalUserList(sjTotal.toString())
                .newUserList(sjNew.toString())
                .build();
        log.info("统计的用户数据:{}", userReportVO);
        return Result.success(userReportVO);
    }
    /**
     * 导出报表
     * @author: XieMingHui
     * @param: [request]
     * @return: 导出成功或失败
     **/
    @Override
    public Result<String> export(HttpServletResponse response) {
        // 1.前端没有传递时间,自定义时间范围
        LocalDate begin = LocalDate.now().minusDays(30);
        LocalDate end = LocalDate.now().minusDays(1);
        // 2.获取需要导出的参数
        // 获取时间范围的营业额
        TurnoverReportVO turnoverData = turnoverStatistics(begin, end).getData();
        // 每天的营业额
        String[] dayTurnover = turnoverData.getTurnoverList().split(",");
        // 总的营业额
        BigDecimal totalTurnover = BigDecimal.valueOf(0.00);
        for (String turnover : dayTurnover) {
            NumberUtil.add(totalTurnover,NumberUtil.parseDouble(turnover));
        }
        // 获取订单数据
        OrderReportVO orderData = ordersStatistics(begin, end).getData();
        // 获取时间明细
        String[] timeArr = orderData.getDateList().split(",");
        // 总的有效订单
        Integer validOrderCount = orderData.getValidOrderCount();
        // 每天有效订单
        String[] dayValidOrders = orderData.getValidOrderCountList().split(",");
        // 每天的总订单
        String[] dayTotalOrders = orderData.getOrderCountList().split(",");
        // 平均客单价
        BigDecimal totalAvg = NumberUtil.div(totalTurnover, validOrderCount);
        // 每天新增用户数
        String[] newUserArr = userStatistics(begin, end).getData().getNewUserList().split(",");
        // 新增用户总数
        int userCount = 0;
        for (String num : newUserArr) {
            userCount=userCount+Integer.parseInt(num);
        }

        //3.创建输入流读取Excel模板文件
        InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("template/运营数据报表.xlsx");
        if (inputStream==null){
            log.info("模板文件不存在");
            throw new BaseException("模板文件不存在");
        }
        try {
            //基于提供好的模板文件创建一个新的Excel表格对象
            XSSFWorkbook excel = new XSSFWorkbook(inputStream);
            //获得Excel文件中的一个Sheet页
            XSSFSheet sheet = excel.getSheetAt(0);
            // 写入标题下的统计时间
            sheet.getRow(1).getCell(0).setCellValue(begin + "至" + end);
            //获得第4行
            XSSFRow row = sheet.getRow(3);
            //获取单元格 填充对应的数据
            row.getCell(1).setCellValue(totalTurnover.doubleValue()); //总营业额
            row.getCell(3).setCellValue(orderData.getOrderCompletionRate()); //总订单完成率(从订单数据中获取)
            row.getCell(5).setCellValue(userCount);//总的新增用户数
            //获取第5行
            row = sheet.getRow(4);
            row.getCell(1).setCellValue(validOrderCount); //有效订单
            row.getCell(3).setCellValue(totalAvg.doubleValue());//平均客单价

            // 循环设置每一行和每一格填充详细数据
            for (int i = 0; i < timeArr.length; i++) {
                // 从第8行开始循环写每行的数据,索引为7
                row = sheet.getRow(7 + i);
                row.getCell(0).setCellValue(timeArr[i]); //日期
                row.getCell(1).setCellValue(dayTurnover[i]);//营业额
                row.getCell(2).setCellValue(dayValidOrders[i]);//有效订单
                //订单完成率(每天有效/每天总订单) 防止除0异常
                if (dayTotalOrders[i].equals("0")){
                    row.getCell(3).setCellValue("0.00");
                }else {
                    row.getCell(3).setCellValue(NumberUtil.div(dayValidOrders[i], dayTotalOrders[i], 2).doubleValue());
                }
                //平均客单价(每天总营业额/每天有效单)防止除0异常
                if (dayValidOrders[i].equals("0")){
                    row.getCell(4).setCellValue("0.00");
                }else {
                    row.getCell(4).setCellValue(NumberUtil.div(dayTurnover[i],dayValidOrders[i],2).doubleValue());
                }
                row.getCell(5).setCellValue(newUserArr[i]);//新增用户数
            }
            //通过输出流将文件下载到客户端浏览器中
            ServletOutputStream out = response.getOutputStream();
            excel.write(out);
            //关闭资源
            out.flush();
            excel.close();
            out.close();
        }catch (IOException e){
            e.printStackTrace();
            throw new BaseException("导出失败");
        }
        return Result.success("导出成功");
    }

    /**
     * 整理数据库查询数据,计算,并封装成VO对象
     *
     * @author: XieMingHui
     * @param: [orderReportAggrDTOS]
     * @return: com.sky.vo.OrderReportVO
     **/
    private OrderReportVO getOrderReportVO(List<OrderReportAggrDTO> orderList, LocalDate begin, LocalDate end) {
        // 调用方法得到总订单集合
        TreeMap<LocalDate, Integer> totalOrder = statsOrder(begin, end);
        // 调用方法得到有效订单集合
        TreeMap<LocalDate, Integer> validOrder = statsOrder(begin, end);
        // 有效订单计数器
        int validCount = 0;
        for (OrderReportAggrDTO orderReportAggrDTO : orderList) {
            LocalDate orderTime = orderReportAggrDTO.getOrderTime();
            // 获取每个订单数据判断状态统计订单,等于5,有效订单
            if (orderReportAggrDTO.getStatus() == 5) {
                validCount++;
                // 有效订单统计
                validOrder.merge(orderTime, 1, Integer::sum);
            }
            // 总订单统计
            totalOrder.merge(orderTime, 1, Integer::sum);
        }
        //  1 总订单数
        int total = orderList.size();
        // 有效订单率
        double rateOforder = NumberUtil.div(validCount, total, 2);
        // 转换所有订单统计的值为字符串格式例如:1,3,2,1
        StringJoiner sjTotal = new StringJoiner(",");
        totalOrder.forEach((localDate, integer) -> sjTotal.add("" + integer));
        // 转换有效订单统计的值为字符串格式
        StringJoiner sjValid = new StringJoiner(",");
        totalOrder.forEach((localDate, integer) -> sjValid.add("" + integer));
        // 转字符串
        String timeStr = CollUtil.join(totalOrder.keySet(), ",");
        // 封装对象返回前端
        OrderReportVO orderReportVO = OrderReportVO.builder()
                .dateList(timeStr)
                .orderCountList(sjTotal.toString())
                .validOrderCountList(sjValid.toString())
                .totalOrderCount(total)
                .validOrderCount(validCount)
                .orderCompletionRate(rateOforder)
                .build();
        log.info("订单数据统计:{}", orderReportVO);
        return orderReportVO;
    }
}
